package com.xiaomaoguai.fcp.pre.kepler.convert.core.support.adapter;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.xiaomaoguai.fcp.pre.kepler.convert.anno.ConvertNode;
import com.xiaomaoguai.fcp.pre.kepler.convert.anno.ConvertParam;
import com.xiaomaoguai.fcp.pre.kepler.convert.utils.ToStringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;

/**
 * @fileName: BuildDataDefaultMethod.java
 * @author: WeiHui
 * @date: 2018/11/26 10:51
 * @version: v1.0.0
 * @since JDK 1.8
 */
public class BuildDataDefaultMethod extends AbstractConvertMethod {

	@Override
	public boolean canHandler(Method method) {
		return StringUtils.indexOfAny(method.getName(), "parserAvExpression", "buildDataByEl", "buildDataByNodeName", "convert") == -1 && method.getParameterCount() >= 1;
	}

	/**
	 * 扩展成注解加值的方式, 参数需要加 @ConvertParam,当有且仅有一个时，可以不加这个注解
	 *
	 * @param method 方法
	 * @param args   入参
	 * @return 转换结果
	 */
	@Override
	public Object invoke(Method method, Object[] args) {
		if (ToStringUtils.TO_STRING.equals(method.getName())) {
			return ToStringUtils.handlerToString(this, method, args);
		}
		ConvertNode convertNode = method.getAnnotation(ConvertNode.class);
		if (convertNode == null) {
			throw new IllegalArgumentException("method missing @ConvertNode annotation");
		}
		String el = convertNode.value();
		if (StringUtils.isBlank(el)) {
			throw new IllegalArgumentException("@ConvertNode el expression is blank,please check!");
		}
		Map<String, Object> env = buildEnv(method, args);
		if (containsAv(method)) {
			Expression expression = AviatorEvaluator.compile(el, true);
			el = (String) expression.execute(env);
		}
		return buildData(el, env, method.getReturnType());
	}

	/**
	 * 构建解析环境参数
	 *
	 * @param method 方法
	 * @param args   参数
	 * @return 解析环境参数
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Object> buildEnv(Method method, Object[] args) {
		Map<String, Object> env = Maps.newHashMapWithExpectedSize(16);
		Parameter[] parameters = method.getParameters();
		if (ArrayUtils.isNotEmpty(parameters)) {
			for (int i = 0; i < parameters.length; i++) {
				Parameter p = parameters[i];
				if (p.isAnnotationPresent(ConvertParam.class)) {
					String value = p.getAnnotation(ConvertParam.class).value();
					if (StringUtils.isNotBlank(value)) {
						env.put(value, args[i]);
					} else {
						Map argMap = JSON.parseObject(JSON.toJSONString(args[i]), env.getClass());
						argMap.forEach((k, v) -> env.put(k.toString(), v));
					}
				} else {
					Map argMap = JSON.parseObject(JSON.toJSONString(args[i]), env.getClass());
					argMap.forEach((k, v) -> env.put(k.toString(), v));
				}
			}
		}
		return env;
	}

	/**
	 * 参数上是否含有 ConvertParam 注解
	 *
	 * @param method 方法名
	 * @return true-含有， false-不含
	 */
	private boolean containsAv(Method method) {
		Parameter[] parameters = method.getParameters();
		if (ArrayUtils.isNotEmpty(parameters)) {
			for (Parameter p : parameters) {
				if (p.isAnnotationPresent(ConvertParam.class)) {
					return true;
				}
			}
		}
		return false;
	}

}
